﻿using System;
using System.Net;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Services;
using System.Xml;
using System.Reflection;
using System.Web.Script.Services;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;

[WebService(Namespace = "http://www.lexisnexis.co.uk/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line. 
[System.Web.Script.Services.ScriptService]

public class Service : System.Web.Services.WebService
{
    SqlConnection objSqlConnection;
    string sqlStr;

    public Service()
    {

        //Uncomment the following line if using designed components 
        //InitializeComponent(); 

        objSqlConnection = new SqlConnection(ConfigurationManager.ConnectionStrings["GenericBrokerEntities"].ConnectionString);
    }

    [WebMethod(EnableSession = true)]
    [ScriptMethod(UseHttpGet = true)]
    public string ResetDataChannel()
    {
        string result, status, message;
        result = ""; status = "OK"; message = "Channel has been reset";
        result += "<brokerResponse>";

        try
        {
            Session["data"] = "";
        }
        catch (Exception ex)
        {
            status = "Error";
            message = ex.Message;
            goto FinishResetDataChannel;
        }

    FinishResetDataChannel:
        result += "<brokerStatus>" + status + "</brokerStatus>";
        result += "<brokerMessage>" + message + "</brokerMessage>";
        result += "</brokerResponse>";

        return result;
    }

    [WebMethod(EnableSession = true)]
    [ScriptMethod(UseHttpGet = true)]
    public string AddDataToChannel(string data)
    {
        string result, status, message;
        result = ""; status = "OK"; message = "Data appended to channel";
        result += "<brokerResponse>";

        try
        {
            Session["data"] += data;
        }
        catch (Exception ex)
        {
            status = "Error";
            message = ex.Message;
            goto FinishAddDataToChannel;
        }

    FinishAddDataToChannel:
        result += "<brokerStatus>" + status + "</brokerStatus>";
        result += "<brokerMessage>" + message + "</brokerMessage>";
        result += "</brokerResponse>";

        return result;
    }

    [WebMethod(EnableSession = true)]
    [ScriptMethod(UseHttpGet = true)]
    public string DisplayDataChannel()
    {
        string result, status, message;
        result = ""; status = "OK"; message = "";
        result += "<brokerResponse>";

        try
        {
            result += "<sessionData><![CDATA[" + Session["data"] + "]]></sessionData>";
        }
        catch (Exception ex)
        {
            status = "Error";
            message = ex.Message;
            goto FinishAddDataToChannel;
        }

    FinishAddDataToChannel:
        result += "<brokerStatus>" + status + "</brokerStatus>";
        result += "<brokerMessage>" + message + "</brokerMessage>";
        result += "</brokerResponse>";

        return result;
    }

    [WebMethod(EnableSession = true)]
    [ScriptMethod(UseHttpGet = true)]
    public string ProcessBufferedRequest()
    {
        return ProcessImmediate(Session["data"].ToString());
    }

    [WebMethod(EnableSession = true)]
    [ScriptMethod(UseHttpGet = true)]
    public string ProcessImmediate(string requestXml)
    {
        // declare and initialise objects
        string result, status, message;
        Type classType;
        object serviceClass;
        object[] paramValue;
        object invocationMethodResult = null;
        XmlDocument objXmlDocument = new XmlDocument();
        string direction = "", serviceClassValue = "", invocationMethodValue = "", data = "", resultOnly = "", userName = "", password = ""; 

        result = ""; status = "OK"; message = "";
        result += "<brokerResponse>";

        // validate xml structure
        try
        {
            objXmlDocument.LoadXml(requestXml);
        }
        catch (Exception ex)
        {
            status = "Error";
            message = "Invalid or missing xml";
            goto FinishProcessRequest;
        }

        // extract and validate nodes
        try
        {
            try { direction = objXmlDocument.SelectSingleNode("/brokerRequest/direction").InnerText; }
            catch { }
        
            try { serviceClassValue = objXmlDocument.SelectSingleNode("/brokerRequest/serviceClass").InnerText; }
            catch { }

            try { invocationMethodValue = objXmlDocument.SelectSingleNode("/brokerRequest/invocationMethod").InnerText; }
            catch { }

            try { data = objXmlDocument.SelectSingleNode("/brokerRequest/data").InnerText; }
            catch { }

            try { resultOnly = objXmlDocument.SelectSingleNode("/brokerRequest/resultOnly").InnerText; }
            catch { }

            try { userName = objXmlDocument.SelectSingleNode("/brokerRequest/userName").InnerText; }
            catch { }

            try { password = objXmlDocument.SelectSingleNode("/brokerRequest/password").InnerText; }
            catch { }

            try { Session["companyCode"] = objXmlDocument.SelectSingleNode("/brokerRequest/companyCode").InnerText; }
            catch { }

            if (direction == "")
            {
                status = "Error";
                message = "[direction] node not supplied";
                goto FinishProcessRequest;
            }
            if (serviceClassValue == "")
            {
                status = "Error";
                message = "[serviceClass] node not supplied";
                goto FinishProcessRequest;
            }
            if (invocationMethodValue == "")
            {
                status = "Error";
                message = "[invocationMethod] node not supplied";
                goto FinishProcessRequest;
            }
        }
        catch (Exception ex)
        {
            status = "Error";
            message = ex.Message;
            goto FinishProcessRequest;
        }

        // this is where you would perform any proprietary credentials checking
        // START - check userName/password
        // ...
        // END - check userName/password

        // attempt to create requested service object
        try
        {
            classType = TypeDelegator.GetType(serviceClassValue);
            serviceClass = Activator.CreateInstance(classType);
        }
        catch (Exception ex)
        {
            status = "Error";
            message = "Unable to create class [" + serviceClassValue + "] - " + ex.Message;
            goto FinishProcessRequest;
        }

        // attempt to invoke requested method
        try
        {
            paramValue = new object[1];
            paramValue[0] = data;
            Session["status"] = "OK";
            Session["message"] = "";
            invocationMethodResult = classType.InvokeMember(invocationMethodValue, BindingFlags.InvokeMethod, null, serviceClass, paramValue);
            status = (string)Session["status"];
            if (status == "OK")
            {
                message += (message == "" ? "" : " - ") + "Invocation method executed OK";
            }
            else
            {
                message = (string)Session["message"];
                goto FinishProcessRequest;
            }
        }
        catch (Exception ex)
        {
            status = "Error";
            message = "Unable to invoke [" + invocationMethodValue + "] - " + ex.Message;
            goto FinishProcessRequest;
        }


    FinishProcessRequest:
        try
        {
            string createdBy = ((userName != "") ? userName : HttpContext.Current.Request.LogonUserIdentity.Name); // System.Security.Principal.WindowsIdentity.GetCurrent().Name
            if (objSqlConnection.State != ConnectionState.Open) objSqlConnection.Open();
            sqlStr = "INSERT INTO [Packets] ([IPAddress], [Direction], [Data], [ServiceClass], [InvocationMethod], [InvocationMethodResult], [Status], [Message], [CreatedBy]) ";
            sqlStr += "VALUES(";
            sqlStr += "'" + Infrastructure.GetIP4Address() + "',";
            sqlStr += "N'" + direction + "',";
            sqlStr += "N'" + Infrastructure.StripXmlHeader(requestXml).Replace("'", "''") + "',";
            sqlStr += "N'" + serviceClassValue + "',";
            sqlStr += "N'" + invocationMethodValue + "',";
            sqlStr += "N'" + (invocationMethodResult == null ? "" : Infrastructure.StripXmlHeader(invocationMethodResult.ToString()).Replace("'", "''")) + "',";
            sqlStr += "N'" + status + "',";
            sqlStr += "N'" + message.Replace("'", "''") + "',";
            sqlStr += "N'" + createdBy + "'";
            sqlStr += ");";
            sqlStr += "SELECT SCOPE_IDENTITY();";
            decimal packetId = (decimal)(new SqlCommand(sqlStr, objSqlConnection)).ExecuteScalar();
            TraverseNodes(objXmlDocument.ChildNodes, packetId, createdBy);
        }
        catch (Exception ex)
        {
            status = "Error";
            message = ex.Message;
        }

        // reset session data for next process cycle
        Session["data"] = "";

        // response terminator
        result += "<brokerStatus>" + status + "</brokerStatus>";
        result += "<brokerMessage>" + message + "</brokerMessage>";
        //result += "<invocationMethodResponse><![CDATA[" + (invocationMethodResult == null ? "" : invocationMethodResult.ToString()) + "]]></invocationMethodResponse>"; // Some requesters unable to deal with CDATA tag
        result += "<invocationMethodResponse>" + (invocationMethodResult == null ? "" : invocationMethodResult.ToString()) + "</invocationMethodResponse>";
        result += "</brokerResponse>";

        if (objSqlConnection.State != ConnectionState.Closed) objSqlConnection.Close();
        if (resultOnly == "true")
            return invocationMethodResult.ToString();
        else
            return result;
    }

    private void TraverseNodes(XmlNodeList nodes, decimal packetId, string createdBy)
    {
        foreach (XmlNode node in nodes)
        {
            if (node.NodeType == XmlNodeType.Element)
            {
                if (node.HasChildNodes)
                {
                    if (node.FirstChild.NodeType == XmlNodeType.Text)
                    {
                        (new SqlCommand("INSERT INTO [PacketData] (PacketId, [Key], [Value], [CreatedBy]) VALUES(" + packetId.ToString() + ",N'" + GetXPathToNode(node) + "',N'" + node.FirstChild.Value.Replace("'", "") + "','" + createdBy + "')", objSqlConnection)).ExecuteNonQuery();
                    }
                }
            }
            TraverseNodes(node.ChildNodes, packetId, createdBy);
        }
    }

    private static string GetXPathToNode(XmlNode node)
    {
        string result = "";

        XmlNode objXmlNode = node;
        result = node.Name;
        while (node.ParentNode.Name != "#document")
        {
            node = node.ParentNode;
            result = node.Name + "_" + result;
        }

        return result;
    }
}
